package jocl.samples;

import java.util.Arrays;

import org.jocl.*;

import static org.jocl.CL.*;

/**
 * Created with IntelliJ IDEA.
 * User: Duy Chung
 * Date: 9/15/13
 * Time: 10:21 PM
 * To change this template use File | Settings | File Templates.
 */
public class Computation implements Runnable {

    private cl_kernel kernel;
    private cl_command_queue command;
    private cl_device_id device;
    private cl_context context;
    private int size;
    float input[];
    private cl_mem inputMem;
    private cl_mem outputMem;
    cl_program program;
    cl_event events[];
    boolean done = false;
    float output[];
    int numberOfDevices;
    private String name;

    public Computation(int size,
                       int numberOfDevices,
                       cl_context context,
                       cl_program program,
                       cl_device_id device,
                       String name) {
        this.device = device;
        this.size = size;
        this.numberOfDevices = numberOfDevices;
        this.context = context;
        this.program = program;
        this.name = name;
        input = new float[size];
        Arrays.fill(input, 1.123f);
        output = new float[size/numberOfDevices];
        init();

    }

    private void init() {
        kernel = clCreateKernel(program, "sampleKernel", null);
        inputMem = clCreateBuffer(context,
                                          CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
                                          Sizeof.cl_float * size,
                                          Pointer.to(input), null);

        outputMem = clCreateBuffer(context,
                                      CL_MEM_READ_WRITE,
                                      Sizeof.cl_float * (size/this.numberOfDevices) ,
                                      null, null);
        long properties = 0;
        properties |= CL.CL_QUEUE_PROFILING_ENABLE;
        command = clCreateCommandQueue(context, device, properties, null);
        events = new cl_event[1];
        clSetKernelArg(kernel, 0, Sizeof.cl_mem, Pointer.to(inputMem));
        clSetKernelArg(kernel, 1, Sizeof.cl_mem, Pointer.to(outputMem));
        clSetKernelArg(kernel, 2, Sizeof.cl_int, Pointer.to(new int[]{size / this.numberOfDevices}));

        events[0] = new cl_event();

    }


    // run thread here
    public void run() {

//        long before = System.nanoTime();

        clEnqueueNDRangeKernel(command,
                kernel,
                1,
                null,
                new long[]{size/ this.numberOfDevices}, // global work size
                null,
                0,
                null,
                events[0]);

        clWaitForEvents(events.length, events);
//        long after = System.nanoTime();
//        float totalDurationMs = (after-before)/1e6f;
//        System.out.println("Total duration of " + getName() + ": " +totalDurationMs+"ms");
        float durationMs = computeDurationMs(events[0]);
        System.out.println("Duration on device " + getName() + ": " + durationMs + "ms");
//        clEnqueueReadBuffer(command, outputMem, CL_TRUE, 0,
//                    size/3 * Sizeof.cl_float, Pointer.to(output), 0, null, null);


        releaseResources();
        System.out.println("Thread: " + getName() + " finished!!!!");

    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    // release resource
    public void releaseResources() {
        clReleaseKernel(kernel);
        clReleaseProgram(program);
        clReleaseMemObject(inputMem);
        clReleaseMemObject(outputMem);
        clReleaseEvent(events[0]);
        clReleaseCommandQueue(command);
        clReleaseContext(context);
    }

    private static float computeDurationMs(cl_event event)
    {
        long startTime[] = {0};
        long endTime[] = {0};
        CL.clGetEventProfilingInfo(
                event, CL.CL_PROFILING_COMMAND_START,
                Sizeof.cl_ulong, Pointer.to(startTime), null);
        CL.clGetEventProfilingInfo(
                event, CL.CL_PROFILING_COMMAND_END,
                Sizeof.cl_ulong, Pointer.to(endTime), null);
        long durationNs = endTime[0]-startTime[0];
        return durationNs / 1e6f;
    }

}
